package com.smileus.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

/**
 * 反射的Utils函数集合.
 * 
 * 提供访问私有变量,获取范型类型Class,提取集合中元素的属性等Utils函数.
 * 
 * @author ben
 */
@SuppressWarnings("unchecked")
public class ReflectionUtil {

	private static Logger logger = LoggerFactory.getLogger(ReflectionUtil.class);

	private static final Map<Class, Map<String, Field>> fieldsMap = new HashMap<Class, Map<String, Field>>();

	private ReflectionUtil() {
	}

	public static void putField(Class clazz, String fieldName, Field field) {
		Map<String, Field> fields = fieldsMap.get(clazz);
		if (fields == null) {
			fields = new HashMap<String, Field>();
			fieldsMap.put(clazz, fields);
		}

		fields.put(fieldName, field);
	}

	public static Field getField(Class clazz, String fieldName) {
		Map<String, Field> fields = fieldsMap.get(clazz);
		if (fields == null) {
			return null;
		}

		return fields.get(fieldName);
	}

	/**
	 * 直接读取对象属性值,无视private/protected修饰符,不经过getter函数.
	 */
	public static Object getFieldValue(final Object object, final String fieldName) {
		Field field = getDeclaredField(object, fieldName);

		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
		}

		Object result = null;
		try {
			result = field.get(object);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			logger.error("反射取值异常：", e.getMessage());
		}

		return result;
	}

	/**
	 * 直接设置对象属性值,无视private/protected修饰符,不经过setter函数.
	 */
	public static void setFieldValue(final Object object, final String fieldName, final Object value) {
		Field field = getDeclaredField(object, fieldName);

		if (field == null)
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");

		try {
			field.set(object, value);
		} catch (IllegalAccessException e) {
			logger.error("不可能抛出的异常:{}", e.getMessage());
		}
	}

	/**
	 * 循环向上转型,获取类的DeclaredField.
	 */
	public static Field getDeclaredField(final Object object, final String fieldName) {
		Assert.notNull(object);
		Assert.hasText(fieldName);
		Class clazz = object.getClass();

		if (getField(clazz, fieldName) != null) {
			return getField(clazz, fieldName);
		}

		for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				Field field = superClass.getDeclaredField(fieldName);
				field.setAccessible(true);
				putField(clazz, fieldName, field);
				return field;
			} catch (NoSuchFieldException e) {
				StringBuilder sb = new StringBuilder(clazz.getName()).append("中没有属性：").append(fieldName);
				logger.error(sb.toString(), e.getMessage());
			}
		}
		return null;
	}

	/**
	 * 循环向上转型,获取类的DeclaredField.
	 */
	public static Map<String, Field> getDeclaredFieldMap(Object valueBean) {
		Assert.notNull(valueBean);
		Class clazz = valueBean.getClass();
		Map<String, Field> fieldMap = new HashMap<String, Field>();
		Field[] fields = clazz.getDeclaredFields();
		Method[] methods = clazz.getDeclaredMethods();
		for (Field field : fields) {
			String filedName = field.getName();
			String methodName = getMethodName(filedName);
			for (Method method : methods) {
				if (methodName.equals(method.getName())) {
					field.setAccessible(true);
					fieldMap.put(filedName, field);
				}
			}
		}
		return fieldMap;
	}

	private static String getMethodName(String fieldName) {
		return new StringBuilder("get").append(fieldName.substring(0, 1).toUpperCase()).append(fieldName.substring(1)).toString();
	}

	public static void main(String[] args) {
		System.out.println(getMethodName("age"));
	}

	/**
	 * 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public UserDao extends HibernateDao<User>
	 * 
	 * @param clazz
	 *            The class to introspect
	 * @return the first generic declaration, or Object.class if cannot be
	 *         determined
	 */
	public static Class getSuperClassGenricType(Class clazz) {
		return getSuperClassGenricType(clazz, 0);
	}

	/**
	 * 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public UserDao extends
	 * HibernateDao<User,Long>
	 * 
	 * @param clazz
	 *            clazz The class to introspect
	 * @param index
	 *            the Index of the generic ddeclaration,start from 0.
	 * @return the index generic declaration, or Object.class if cannot be
	 *         determined
	 */

	public static Class getSuperClassGenricType(Class clazz, int index) {

		Type genType = clazz.getGenericSuperclass();

		if (!(genType instanceof ParameterizedType)) {
			logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
			return Object.class;
		}

		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

		if (index >= params.length || index < 0) {
			logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + params.length);
			return Object.class;
		}
		if (!(params[index] instanceof Class)) {
			logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
			return Object.class;
		}
		return (Class) params[index];
	}

	public static Object copy(Object to, Object from) {
		Class toType = to.getClass();
		Field toFields[] = toType.getDeclaredFields();
		Class fromType = from.getClass();
		Field fromFields[] = fromType.getDeclaredFields();
		// 复制
		for (int i = 0; i < fromFields.length; i++) {
			try {
				String fromFieldName = fromFields[i].getName();
				if (hasField(toFields, fromFieldName)) {
					Method getMethod = getMethodByGet(fromType, fromFieldName);
					Method setMethod = getMethodBySet(toType, fromFieldName, fromFields[i]);

					Object value = getMethod.invoke(from, new Object[] {});
					if (value != null)
						setMethod.invoke(to, new Object[] { value });
				}
			} catch (Exception e) {
				logger.warn(e.toString());
				continue;
			}
		}
		return to;
	}

	/**
	 * 得到get方法
	 * 
	 * @param classType2
	 * @param fieldName2
	 * @throws NoSuchMethodException
	 */
	private static Method getMethodByGet(Class classType, String fieldName) throws NoSuchMethodException {
		// 第一个字母大写
		String firestLetter = fieldName.substring(0, 1).toUpperCase();

		// 得到getXXX方法名
		String methodName = "get" + firestLetter + fieldName.substring(1);

		// 得到对应的方法
		Method method = classType.getMethod(methodName, new Class[] {});

		return method;
	}

	/**
	 * 得到set方法
	 * 
	 * @param classType
	 * @param fieldName
	 * @return
	 * @throws NoSuchMethodException
	 */
	private static Method getMethodBySet(Class classType, String fieldName, Field type) throws NoSuchMethodException {
		// 第一个字母大写
		String firestLetter = fieldName.substring(0, 1).toUpperCase();

		// 得到getXXX方法名
		String methodName = "set" + firestLetter + fieldName.substring(1);

		// 得到对应的方法
		Method method = classType.getMethod(methodName, new Class[] { type.getType() });

		return method;
	}

	/**
	 * 判断有无属性
	 * 
	 * @param fields1
	 * @param fieldName2
	 */
	private static boolean hasField(Field[] fields, String fieldName) {
		for (int j = 0; j < fields.length; j++) {
			if (fieldName.equalsIgnoreCase(fields[j].getName())) {
				return true;
			}
		}
		return false;
	}
}
